package com.zhl.projectmerger.strategy;

import com.zhl.projectmerger.MergeStrategy;
import com.zhl.projectmerger.ProjectMergeConfig;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;

public abstract class AbtractMergeStrategy implements MergeStrategy {
    @Override
    public boolean match(ClassOrInterfaceDeclaration fromClass, ClassOrInterfaceDeclaration toClass, ProjectMergeConfig mergerConfig) {
        return false;
    }

    /**
     * 导入处理函数，将源编译单元中的导入语句导入到目标编译单元中。
     * 如果目标编译单元中不存在相同的导入语句，则将其添加到目标编译单元中。
     *
     * @param from 源编译单元
     * @param to   目标编译单元
     */
    @Override
    public void importProcess(CompilationUnit from, CompilationUnit to) {
        from.getImports().forEach(importDeclaration -> {
            if (to.getImports().stream().noneMatch(
                    importDeclaration1 -> importDeclaration1.getName().equals(importDeclaration.getName()))
            ) {
                to.addImport(importDeclaration);
            }
        });
    }

    @Override
    public void classFileAnnotationProcess(ClassOrInterfaceDeclaration from, ClassOrInterfaceDeclaration to) {
        // 对于只存在于from中的注解，添加到to中，判断依据是，根据注解的名称比较. @RestController("abc") 的名称为 RestController
        // 例如：@RestController("abc") 和 @RestController("abc") 这样的需要自行在具体的策略里面写逻辑处理
        // from 和 to 都有注解，则自行写逻辑处理合并
        from.getAnnotations().forEach(annotationExpr -> {
            if (to.getAnnotations().stream().noneMatch(annotationExpr1 -> annotationExpr1.getName().equals(annotationExpr.getName()))) {
                to.addAnnotation(annotationExpr);
            }
        });
    }

    /**
     * 合并字段声明到目标类中。
     *
     * @param toClass 目标类，即要合并字段声明的类
     * @param fieldDeclaration 待合并的字段声明
     */
    @Override
    public void fieldsMerge(ClassOrInterfaceDeclaration toClass, FieldDeclaration fieldDeclaration) {
        // 如果该字段在目标类中不存在，则添加，如果存在，则进行注解合并， 根据属性的名称作为判断依据。
        if (!toClass.getFieldByName(fieldDeclaration.getVariables().get(0).getNameAsString()).isPresent()) {
            toClass.getMembers().add(fieldDeclaration);
        } else {
            // 如果目标类中存在该字段，则交由具体的策略实现类处理
            FieldDeclaration toFieldDeclaration = toClass.getFieldByName(fieldDeclaration.getVariables()
                    .get(0).getNameAsString()).get();
            doFieldsMerge(fieldDeclaration, toFieldDeclaration);
        }
    }

    /**
     * 抽象方法，由具体的策略实现类实现
     * @param fromFieldDeclaration
     * @param toFieldDeclaration
     */
    protected abstract void doFieldsMerge(FieldDeclaration fromFieldDeclaration, FieldDeclaration toFieldDeclaration);

    /**
     * 将给定方法声明合并到目标类中。
     *
     * @param toClass        目标类，即要合并方法的类
     * @param methodDeclaration  待合并的方法声明
     */
    @Override
    public void methodMerge(ClassOrInterfaceDeclaration toClass, MethodDeclaration methodDeclaration) {
        String methodName = methodDeclaration.getNameAsString();
        NodeList<Parameter> parameters = methodDeclaration.getParameters();
        String[] parameterTypes = new String[parameters.size()];
        for (int i = 0; i < parameters.size(); i++) {
            String typeName = parameters.get(i).getType().asString();
            parameterTypes[i] = typeName;
        }

        if (!toClass.getMethodsBySignature(methodName, parameterTypes).isEmpty()) {
            // 如果目标类中存在该方法，则交由具体的策略实现类处理
            MethodDeclaration toMethodDeclaration = toClass.getMethodsBySignature(methodName, parameterTypes).get(0);
            doMethodMerge(methodDeclaration, toMethodDeclaration);
        } else {
            // 如果目标类中不存在该方法，则添加
            toClass.getMembers().add(methodDeclaration);
        }
    }

    /**
     * 执行方法合并操作
     *
     * @param fromMethodDeclaration 源方法声明
     * @param toMethodDeclaration 目标方法声明
     * @throws Exception 如果合并过程中出现任何异常
     */
    protected abstract void doMethodMerge(MethodDeclaration fromMethodDeclaration, MethodDeclaration toMethodDeclaration);
}
